/*
    This file is part of Raptor.

    Raptor is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Raptor is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Raptor.  If not, see <http://www.gnu.org/licenses/>.

 */

#include "container.h"

#include <QStringList>
#include <QFile>
//#include <QtCrypto>

container::container()
{
	/*

	CFB8-AES128.Encrypt
	Key            2b7e151628aed2a6abf7158809cf4f3c
	IV             000102030405060708090a0b0c0d0e0f
	Segment #1
	Input Block    000102030405060708090a0b0c0d0e0f
	Output Block   50fe67cc996d32b6da0937e99bafec60
	Plaintext      6b
	Ciphertext     3b

	CFB8-AES128.Decrypt
	Key            2b7e151628aed2a6abf7158809cf4f3c
	IV             000102030405060708090a0b0c0d0e0f
	Segment #1
	Input Block    000102030405060708090a0b0c0d0e0f
	Output Block   50fe67cc996d32b6da0937e99bafec60
	Ciphertext     3b
	Plaintext      6b

	########

	CFB128-AES128.Encrypt
	Key            2b7e151628aed2a6abf7158809cf4f3c
	IV             000102030405060708090a0b0c0d0e0f
	Segment #1
	Input Block    000102030405060708090a0b0c0d0e0f
	Output Block   50fe67cc996d32b6da0937e99bafec60
	Plaintext      6bc1bee22e409f96e93d7e117393172a
	Ciphertext     3b3fd92eb72dad20333449f8e83cfb4a

	CFB128-AES128.Decrypt
	Key            2b7e151628aed2a6abf7158809cf4f3c
	IV             000102030405060708090a0b0c0d0e0f
	Segment #1
	Input Block    000102030405060708090a0b0c0d0e0f
	Output Block   50fe67cc996d32b6da0937e99bafec60
	Ciphertext     3b3fd92eb72dad20333449f8e83cfb4a
	Plaintext      6bc1bee22e409f96e93d7e117393172a

	*/


}

container::~container()
{

}

QStringList container::decryptRSDF(const QString &fileName)
{
	QFile file(fileName);
	return decryptRSDF(file);
}

QStringList container::decryptRSDF(QFile &file)
{

	QStringList tmpStrList = QStringList();

	if(file.exists())
	{
		if(file.open(QIODevice::ReadOnly | QIODevice::Text))
		{
			QByteArray input = "";
			QTextStream inStream(&file);

			while(!inStream.atEnd())
				input.append(inStream.readLine());

			QByteArray data = QByteArray::fromHex(input);

			file.close();

			aes_context ctx;

			unsigned char key[24];
			unsigned char iv[16];

			memcpy(key, QByteArray::fromHex("8C35192D964DC3182C6F84F3252239EB4A320D2500000000"), 24);
			//iv muss nicht immer neu errechnet werden..
			//memcpy(iv, QByteArray::fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), 16);
			memcpy(iv, QByteArray::fromHex("a3d5a33cb95ac1f5cbdb1ad25cb0a7aa"), 16);

			QList<QByteArray> array = data.split('\n');

			aes_setkey_enc(&ctx, key, 192);

/* iv muss nicht immer neu errechnet werden..
			unsigned char in[16];
			memcpy(in, iv, 16);

			unsigned char out[16];
			memset(out, 0, 16);

			aes_crypt_ecb(&ctx, AES_ENCRYPT, in, out);
			memcpy(iv, out, 16);//a3d5a33cb95ac1f5cbdb1ad25cb0a7aa

			//qDebug() << "IV:" << QByteArray((const char *)iv, 16).toHex().data();
*/
			for(int i=0; i<array.count(); i++)
			{
				int len = array.at(i).size()-1;

				if(len > 0)
				{
					unsigned char url_in_base[len];
					unsigned char url_in[len];
					unsigned char url_out[len];

					memcpy(url_in_base, array.at(i), len);
					memset(url_out, 0, len);

					base64_decode(url_in, &len, url_in_base, len);
					cfb_decrypt(&ctx, len, iv, url_in, url_out);

					tmpStrList << QString(QByteArray((const char *)url_out, len)).replace("CCF:", "");
				}
			}
		} else
		{
			qDebug("cant read file");
			return QStringList();
		}
	} else
	{
		qDebug("file does not exists");
		return QStringList();
	}

	return tmpStrList;
}


QStringList container::decryptCCF(const QString &fileName)
{
	/*
Es gibt 5 verschiedene, mir bekannte CCF-Revisionen.
Bei CCF 0.7, 0.8 und 1.0 kommt Rijndael mit 256 Bit langen, statischen Keys im CBC-Modus zum Einsatz. 128 Bit IV
Bei CCF 3.0 wird ein einer Cipher verwendet, der Designfehler aufweist (was allerdings für dein Vorhaben irrelevant ist) und dessen `Key` direkt im Container steht.
CCF 5.0 verwendet (verschachtelt) Rijndael im CBC-Modus.
	 */

	QFile file(fileName);
	return decryptCCF(file);
}

QStringList container::decryptCCF(QFile &file)
{
	QStringList tmpStrList = QStringList();

	if(file.exists())
	{
		if(file.open(QIODevice::ReadOnly | QIODevice::Text))
		{
			QByteArray input = "";
			QTextStream inStream(&file);

			while(!inStream.atEnd())
			{
				input.append(inStream.readLine());
			}

			//qDebug() << QString(QByteArray((const char *)data2, len).toHex());
			//return QStringList();

			file.close();

			input = QByteArray::fromHex("434346332e300703adef2f9a3f086dfc8a06cf61f09aec7d14d118be305220f5c413c554c9bec70d28a57ef62d4136b2954ea719bfb0f16a598765ff61143e9da0508a0cf0b1c35b6bb76fb0731572e6c313cf2ea1d1a37d489e4cad223469e2cf13de5cc9becb4328f07fe26c4d22d4");
			//int len = input.size();

			QByteArray signatur = input.left(6);
			QByteArray magicBytes = input.mid(6, 4);
			QByteArray padding = input.mid(10, 1);
			QByteArray data = input.mid(11);

			quint32 magic = 0xEFAD0307;
			unsigned char buffer[64];
			unsigned char x[64];

			for(int i=0; i<64; i++)
			{
			    if((magic & 1) != 0)
			    {
			        rotateRight(&magic, (magic & 0xff) % 12);
			    } else
			    {
			        rotateLeft(&magic, (magic & 0xff) % 9);
			    }
			    buffer[i] = (unsigned char)(buffer[i] ^ ((unsigned char)(magic & 0xff)));
			    //bigmatr(buffer);
			}

			for(int j=0; j<8; j++)
			{
			    for(int k=0; k<8; k++)
			    {
			        x[k] = buffer[(j * 8) + k];
			    }

			    for(int m=0; m<8; m++)
			    {
			        x[m] = (unsigned char)(x[m] ^ ((unsigned char)(magic & 0xff)));
			        //smallmatr(x);
			        if ((magic & 1) != 0) {
			        	rotateRight(&magic, magic & 12);
			        } else {
			            rotateLeft(&magic, magic & 9);
			        }
			    }
			    for(int n=0; n<8; n++)
			    {
			        buffer[(j * 8) + n] = x[n];
			    }
			}

			qDebug() << QString(magicBytes.toHex().data());
		} else
		{
			qDebug("cant read file");
			return QStringList();
		}
	} else
	{
		qDebug("file does not exists");
		return QStringList();
	}

	return tmpStrList;
}

void container::rotateRight(quint32 *magic, unsigned int cnt)
{
    Q_UNUSED(magic);
    Q_UNUSED(cnt);
}

void container::rotateLeft(quint32 *magic, unsigned int cnt)
{
    Q_UNUSED(magic);
    Q_UNUSED(cnt);
}

QStringList container::decryptDLC(const QString &fileName)
{
	//http://eddysblog.phpnet.us/blog/2008/11/15/dlc-geknackt/
	QFile file(fileName);
	return decryptDLC(file);
}

QStringList container::decryptDLC(QFile &file)
{
	QStringList tmpStrList = QStringList();

	if(file.exists())
	{
		if(file.open(QIODevice::ReadOnly | QIODevice::Text))
		{
			QByteArray input = "";
			QTextStream inStream(&file);

			while(!inStream.atEnd())
				input.append(inStream.readLine());

			file.close();

			QByteArray data = QByteArray::fromHex(input);

			QByteArray fileContent = input.left(input.size() - 88);
			QByteArray fileKey = input.mid(input.size()-88, 88);

			//server key besorgen..

			QByteArray serverKeyB64 = "b2ca395af9de6f8e";
			int len = serverKeyB64.size();

			unsigned char serverKey[len];
			unsigned char responseKey[len];

			memcpy(serverKey, serverKeyB64, len);

			base64_decode(responseKey, &len, serverKey, len);

			aes_context ctx;

			unsigned char key[16];
			unsigned char iv[16];
			memcpy(key, QByteArray("15938425379F64DD"), 16);
			memcpy(iv, QByteArray("0000000000000000"), 16);

			aes_setkey_enc(&ctx, key, 128);
			cfb_decrypt(&ctx, len, iv, responseKey, serverKey);

			unsigned char swap_key[16];
			unsigned char new_key[16];
			memcpy(swap_key, QByteArray("24dbc598bcb9bc39"), 16);

			for(int i=0; i<16; i++)
			{
				new_key[i] = swap_key[i] ^ serverKey[i];
			}

			int len2 = fileContent.size();
			unsigned char contentB64[len2];
			memcpy(contentB64, fileContent, len2);

			unsigned char contentOld[len2];

			base64_decode(contentOld, &len2, contentB64, len2);

			QByteArray contentNew = "";
			contentNew.append(QByteArray((const char *)new_key, len));
			contentNew.append(QByteArray((const char *)contentOld, len2));

			unsigned char contentDecode[len+len2];
			unsigned char contentDecoded[len+len2];
			memcpy(contentDecode, contentNew, len+len2);

			cfb_decrypt(&ctx, len+len2, iv, contentDecoded, contentDecode);
			base64_decode(contentDecode, &len2, contentDecoded, len+len2);

			qDebug() << QString(QByteArray((const char *)contentDecode, len2).toHex().data());

		} else
		{
			qDebug("cant read file");
			return QStringList();
		}
	} else
	{
		qDebug("file does not exists");
		return QStringList();
	}

	return tmpStrList;
}

void container::cfb_decrypt(aes_context *ctx, int length, unsigned char *iv, unsigned char *input, unsigned char *output)
{
	unsigned char input_block[16];
	unsigned char output_block[16];

	//(1st byte)
	memcpy(input_block, iv, 16);
	aes_crypt_ecb(ctx, AES_ENCRYPT, input_block, output_block);
	output[0] = input[0] ^ output_block[0];

	//(2nd...last byte)
	for(int n = 1; n <=length; n++)
	{
		for (int i = 1; i < 16; i++)
		{
			input_block[i-1] = input_block[i];
		}
		input_block[15] = input[n-1];

		if(n < length)
		{
			aes_crypt_ecb(ctx, AES_ENCRYPT, input_block, output_block);
			output[n] = input[n] ^ output_block[0];
		}
	}

	memcpy(iv, input_block, 16);
}

void container::cfb_encrypt(aes_context *ctx, int length, unsigned char iv[16], unsigned char *input, unsigned char *output)
{
   unsigned char input_block[16];
   unsigned char output_block[16];

   //(1st byte)
   memcpy(input_block, iv, 16);
   aes_crypt_ecb(ctx, AES_ENCRYPT, input_block, output_block);
   output[0] = input[0] ^ output_block[0];

   //(2nd...last byte)
   for (int n = 1; n < length; n++)
   {
      for (int i = 1; i < 16; i++)
      {
         input_block[i-1] = input_block[i];
      }
      input_block[15] = output[n-1];

      aes_crypt_ecb(ctx, AES_ENCRYPT, input_block, output_block);
      output[n] = input[n] ^ output_block[0];
   }
}
